// Geometric Tools, Inc.
// http://www.geometrictools.com
// Copyright (c) 1998-2006.  All Rights Reserved
//
// The Wild Magic Library (WM3) source code is supplied under the terms of
// the license agreement
//     http://www.geometrictools.com/License/WildMagic3License.pdf
// and may not be copied or disclosed except in accordance with the terms
// of that agreement.

#include "Wm3FoundationPCH.h"
#include "Wm3Triangles.h"
#include "Wm3CachedVector3fArray.h"
#include "Wm3IntrRay3Triangle3.h"
using namespace Wm3;

WM3_IMPLEMENT_RTTI(Wm3,Triangles,Geometry);
WM3_IMPLEMENT_ABSTRACT_STREAM(Triangles);
WM3_IMPLEMENT_DEFAULT_STREAM(Triangles,Geometry);
WM3_IMPLEMENT_DEFAULT_NAME_ID(Triangles,Geometry);

//----------------------------------------------------------------------------
Triangles::Triangles ()
{
    // The Type value will be assigned by the derived class.
}
//----------------------------------------------------------------------------
Triangles::Triangles (Vector3fArray* pkVertices, IntArray* pkIndices,
    bool bGenerateNormals, bool bCachedNormals)
    :
    Geometry(pkVertices)
{
    // The Type value will be assigned by the derived class.
    Indices = pkIndices;
}
//----------------------------------------------------------------------------
Triangles::~Triangles ()
{
}
//----------------------------------------------------------------------------
bool Triangles::GetModelTriangle (int i, Triangle3f& rkMTri) const
{
    int iV0, iV1, iV2;
    if (GetTriangle(i,iV0,iV1,iV2))
    {
        Vector3f* akVertex = Vertices->GetData();
        rkMTri.V[0] = akVertex[iV0];
        rkMTri.V[1] = akVertex[iV1];
        rkMTri.V[2] = akVertex[iV2];
        return true;
    }
    return false;
}
//----------------------------------------------------------------------------
bool Triangles::GetWorldTriangle (int i, Triangle3f& rkWTri) const
{
    int iV0, iV1, iV2;
    if (GetTriangle(i,iV0,iV1,iV2))
    {
        Vector3f* akVertex = Vertices->GetData();
        rkWTri.V[0] = World.ApplyForward(akVertex[iV0]);
        rkWTri.V[1] = World.ApplyForward(akVertex[iV1]);
        rkWTri.V[2] = World.ApplyForward(akVertex[iV2]);
        return true;
    }
    return false;
}
//----------------------------------------------------------------------------
void Triangles::GenerateNormals (bool bCached)
{
    int iVQuantity;
    Vector3f* akNormal;

    if (!Normals)
    {
        iVQuantity = Vertices->GetQuantity();
        akNormal = WM3_NEW Vector3f[iVQuantity];
        if (bCached)
        {
            Normals = WM3_NEW CachedVector3fArray(iVQuantity,akNormal);
        }
        else
        {
            Normals = WM3_NEW Vector3fArray(iVQuantity,akNormal);
        }
        UpdateModelNormals();
    }
    else
    {
        if (bCached && !Normals->IsExactly(CachedVector3fArray::TYPE))
        {
            iVQuantity = Normals->GetQuantity();
            akNormal = WM3_NEW Vector3f[iVQuantity];
            Normals = WM3_NEW CachedVector3fArray(iVQuantity,akNormal);
            UpdateModelNormals();
        }
        else if (!bCached && Normals->IsExactly(CachedVector3fArray::TYPE))
        {
            iVQuantity = Normals->GetQuantity();
            akNormal = WM3_NEW Vector3f[iVQuantity];
            Normals = WM3_NEW Vector3fArray(iVQuantity,akNormal);
            UpdateModelNormals();
        }
    }
}
//----------------------------------------------------------------------------
void Triangles::UpdateModelNormals ()
{
    if (!Normals)
    {
        return;
    }

    // Calculate normals from vertices by weighted averages of facet planes
    // that contain the vertices.

    int iVQuantity = Vertices->GetQuantity();
    Vector3f* akVertex = Vertices->GetData();
    Vector3f* akNormal = Normals->GetData();
    memset(Normals->GetData(),0,iVQuantity*sizeof(Vector3f));

    int iTQuantity = GetTriangleQuantity();
    int i;
    for (i = 0; i < iTQuantity; i++)
    {
        // get vertex indices
        int iV0, iV1, iV2;
        if (!GetTriangle(i,iV0,iV1,iV2))
        {
            continue;
        }

        // get vertices
        Vector3f& rkV0 = akVertex[iV0];
        Vector3f& rkV1 = akVertex[iV1];
        Vector3f& rkV2 = akVertex[iV2];

        // compute the normal (length provides the weighted sum)
        Vector3f kEdge1 = rkV1 - rkV0;
        Vector3f kEdge2 = rkV2 - rkV0;
        Vector3f kNormal = kEdge1.Cross(kEdge2);

        akNormal[iV0] += kNormal;
        akNormal[iV1] += kNormal;
        akNormal[iV2] += kNormal;
    }

    for (i = 0; i < iVQuantity; i++)
    {
        akNormal[i].Normalize();
    }
}
//----------------------------------------------------------------------------
Triangles::PickRecord::PickRecord (Triangles* pkIObject, float fT,
    int iTriangle, float fBary0, float fBary1, float fBary2)
    :
    Geometry::PickRecord(pkIObject,fT)
{
    Triangle = iTriangle;
    Bary0 = fBary0;
    Bary1 = fBary1;
    Bary2 = fBary2;
}
//----------------------------------------------------------------------------
void Triangles::DoPick (const Ray3f& rkRay, PickArray& rkResults)
{
    if (WorldBound->TestIntersection(rkRay))
    {
        // convert the ray to model-space coordinates
        Ray3f kRay(World.ApplyInverse(rkRay.Origin),
            World.InvertVector(rkRay.Direction));

        // compute intersections with the model-space triangles
        const Vector3f* akVertex = Vertices->GetData();
        int iTQuantity = GetTriangleQuantity();
        for (int i = 0; i < iTQuantity; i++)
        {
            int iV0, iV1, iV2;
            if (!GetTriangle(i,iV0,iV1,iV2))
            {
                continue;
            }

            Triangle3f kTriangle(akVertex[iV0],akVertex[iV1],akVertex[iV2]);
            IntrRay3Triangle3f kIntr(kRay,kTriangle);
            if (kIntr.Find())
            {
                rkResults.Append(WM3_NEW PickRecord(this,kIntr.GetRayT(),i,
                    kIntr.GetTriB0(),kIntr.GetTriB1(),kIntr.GetTriB2()));
            }
        }
    }
}
//----------------------------------------------------------------------------
